/*
 * @Author: 郑子胜(Zechariahzheng)
 * @Date: 2023-09-18 11:33:01
 * @LastEditors: 郑子胜(Zechariahzheng)
 * @LastEditTime: 2024-01-16 11:35:16
 * @Descripttion: 
 */
#include "MyMqtt.h"

const AllTopic *ALL_TOPIC;    // 所有主题
const DeviceInfo *DEVICE_INFO;// 设备信息
const MQTTInfo *MQTT_INFO;    // MQTT信息
bool MSG_FLAG = false;        // 返回消息标志位
bool CMD_FLAG = false;        // 返回命令标志位
bool DO_FLAG = false;         // 执行标志位
String CMD_PAYLOAD;           // 命令主题内容

char PUSH_DEPTH[5];             // 舵机执行深度
char LAST_TIME[5];              // 舵机持续时间

void callback(char *topic, byte *payload, unsigned int length) {
  MQTT_DEBUG_PRINT("Message arrived [");
  MQTT_DEBUG_PRINT(topic);
  MQTT_DEBUG_PRINT("] ");
  for (int i = 0; i < length; i++) {
    MQTT_DEBUG_PRINT((char)payload[i]);
  }
  MQTT_DEBUG_PRINTLN();

  // 处理发送过来的消息
  if (strncmp(topic, ALL_TOPIC->sub_msg_topic, strlen(ALL_TOPIC->sub_msg_topic))==0) {
    // 1. msg主题；返回消息给请求端
    if (strncmp((char *)payload, "getMsg", 6) == 0) {
      MSG_FLAG = true;
      MQTT_DEBUG_PRINTLN("---It's getMsg");
    } else if (strncmp((char *)payload, "startNow", 8) == 0){
      DO_FLAG = true;
      MQTT_DEBUG_PRINTLN("---It's startNow");
      char *tmp = (char *)malloc(length-7);
      memset(tmp, 0, length-7);
      strlcpy(tmp, (char *)(payload+9), length-8);
      String res = String(tmp);
      free(tmp);
      MQTT_DEBUG_PRINTLN(res);
      DynamicJsonDocument doc(512);
      deserializeJson(doc, res);
      if (doc.containsKey("pushDepth"))
        strlcpy(PUSH_DEPTH, doc["pushDepth"], sizeof(PUSH_DEPTH));
      if (doc.containsKey("lastTime"))
        strlcpy(LAST_TIME, doc["lastTime"], sizeof(LAST_TIME));
    }
  } else if (strncmp(topic, ALL_TOPIC->sub_cmd_topic, strlen(ALL_TOPIC->sub_cmd_topic))==0) {
    // 2. cmd主题
    CMD_FLAG = true;
    MQTT_DEBUG_PRINTLN("---It's cmd topic");
    CMD_PAYLOAD = String((char *)payload);
  } else if (strncmp(topic, ALL_TOPIC->sub_bafa_topic, strlen(ALL_TOPIC->sub_bafa_topic))==0) {
    // 3. 巴法主题
    MQTT_DEBUG_PRINTLN("---It's bafa topic");
    if (length == 2) {
      // 打开操作
      if (strncmp((char *)payload, "on", 2) == 0) {
        // 执行动作
        DO_FLAG = true;
        String info = DEVICE_INFO->getInfo();
        DynamicJsonDocument doc(512);
        deserializeJson(doc, info);
        strlcpy(PUSH_DEPTH, doc["onPushDepth"], sizeof(PUSH_DEPTH));
        strlcpy(LAST_TIME, doc["onLastTime"], sizeof(LAST_TIME));
      }
    } else if (length>=3) {
      if (strncmp((char *)payload, "on#", 3) == 0) {
        // 设置深度
        CMD_FLAG = true;
        DynamicJsonDocument doc(256);
        char tmp[4];
        memset(tmp, 0, 4);
        memcpy(tmp, (char *)(payload+3), 2);
        if (length==4) {
          memcpy(tmp, (char *)(payload+3), 3);
        } else {
          memcpy(tmp, (char *)(payload+3), 2);
        }
        MQTT_DEBUG_PRINT("---tmp is:");
        MQTT_DEBUG_PRINTLN(tmp);
        doc["onPushDepth"] = String((char *)tmp);
        doc["offPushDepth"] = String((char *)tmp);
        serializeJson(doc, CMD_PAYLOAD);
      }
      else if (strncmp((char *)payload, "off", 3) == 0) {
        // 关闭操作
        DO_FLAG = true;
        String info = DEVICE_INFO->getInfo();
        DynamicJsonDocument doc(512);
        deserializeJson(doc, info);
        strlcpy(PUSH_DEPTH, doc["offPushDepth"], sizeof(PUSH_DEPTH));
        strlcpy(LAST_TIME, doc["onLastTime"], sizeof(LAST_TIME));
      }
    }
  }
}

void MyMqtt::init(WiFiClient *espClient, const MQTTInfo *mqttInfo, const AllTopic *allTopic, const DeviceInfo *deviceInfo) {
  MQTT_INFO = mqttInfo;
  DEVICE_INFO = deviceInfo;
  ALL_TOPIC = allTopic;
  this->heart_flag = false;
  this->espClient = espClient;
  this->mqttInfo = mqttInfo;
  this->allTopic = allTopic;
  this->client = new PubSubClient(*this->espClient);
  this->client->setServer(this->mqttInfo->mqtt_server, this->mqttInfo->mqtt_server_port);
  this->client->setCallback(callback);
  this->client->setBufferSize(512);

  // 设置ip地址
  IPAddress ipAddress = WiFi.localIP();
  sprintf(this->ip, "%d.%d.%d.%d", ipAddress[0], ipAddress[1], ipAddress[2], ipAddress[3]);
  
  if (!this->client->connected()) {
    this->reconnect();
  }

  // 订阅主题
  MQTT_DEBUG_PRINTLN(this->allTopic->sub_msg_topic);
  this->client->subscribe(this->allTopic->sub_cmd_topic);
  this->client->subscribe(this->allTopic->sub_msg_topic);
  this->client->subscribe(this->allTopic->sub_bafa_topic);

  // 发送上线消息
  DynamicJsonDocument doc(1024);
  doc["sn"] = this->mqttInfo->sn;
  doc["type"] = "online";
  doc["ip"] = this->ip;
  String sendMsg;
  serializeJson(doc, sendMsg);
  MQTT_DEBUG_PRINTLN(sendMsg);
  this->client->publish(this->allTopic->pub_online_topic, sendMsg.c_str());

  // 定时
  this->ticker.attach(this->mqttInfo->keepalive, ticker_trigger, this);
}

void MyMqtt::loop() {
  delay(0);
  if (this->client->connected()) {
    if (MSG_FLAG) {
      // 1. msg主题；返回消息给请求端
      MQTT_DEBUG_PRINTLN("publish Message!");
      this->client->publish(this->allTopic->pub_msg_topic, DEVICE_INFO->getInfo().c_str());
      MSG_FLAG = false;
    }
    if (CMD_FLAG) {
      // 2. cmd主题；设置到fs文件系统中，并返回已完成消息给请求端
      MQTT_DEBUG_PRINTLN("cmd finished!");
      DynamicJsonDocument doc(512);
      deserializeJson(doc, CMD_PAYLOAD);
      // serializeJson(doc, Serial);
      JsonObject docRoot = doc.as<JsonObject>();
      // 先读取fs文件系统中的参数，传递的参数修改，其余使用原始值
      // 读取fs文件系统中的参数
      File configFile = LittleFS.open("/config.json", "r");
      DynamicJsonDocument fsDoc(512);
      if (!configFile) {
        MQTT_DEBUG_PRINTLN("failed to open config file for reading");
      } else {
        auto deserializeError = deserializeJson(fsDoc, configFile);
        if (deserializeError) {
          MQTT_DEBUG_PRINTLN("failed to read config file");
        }
      }
      configFile.close();
      // 修改参数，将接收的参数更换fs文件系统中的参数
      for (JsonPair keyValue: docRoot) {
        fsDoc[keyValue.key().c_str()] = doc[keyValue.key().c_str()];
      }
      // 将修改完的参数写入
      String res;
      serializeJson(fsDoc, res);
      configFile = LittleFS.open("/config.json", "w");
      if (!configFile) {
        MQTT_DEBUG_PRINTLN("failed to open config file for writing");
      } else {
        configFile.print(res);
        configFile.flush();
      }
      configFile.close();
      // 设置到设备信息对象中
      DEVICE_INFO->setInfo(CMD_PAYLOAD);
      this->client->publish(this->allTopic->pub_cmd_topic, "cmd finished!");
      CMD_FLAG = false;
    }
    if (DO_FLAG) {
      // 3. 执行主题；执行
      MQTT_DEBUG_PRINTLN("to do something");
      DEVICE_INFO->doSomething(PUSH_DEPTH, LAST_TIME);
      DO_FLAG = false;
    }
    // 定时发送心跳信息
    if (this->heart_flag) {
      this->heart_flag = false;
      DynamicJsonDocument doc(1024);
      doc["sn"] = this->mqttInfo->sn;
      doc["type"] = "heartbeat";
      doc["ip"] = this->ip;
      String sendMsg;
      serializeJson(doc, sendMsg);
      MQTT_DEBUG_PRINTLN(sendMsg);
      this->client->publish(this->allTopic->pub_online_topic, sendMsg.c_str());
    }
    this->client->loop();
  } else {
    this->reconnect();
  }
}

MyMqtt::~MyMqtt() {
  delete client;
}

void MyMqtt::heartBeat() {
  // 定时到设置心跳标志位
  this->heart_flag = true;
}

void MyMqtt::reconnect() {
  // 连接MQTT
  while (!String(this->mqtt_id).equals("") && !client->connected()) {
    delay(0);
    MQTT_DEBUG_PRINTLN("Attempting MQTT connection...");
    // Attempt to connect
    if (this->client->connect(this->mqtt_id)) {
      MQTT_DEBUG_PRINTLN("connected");
      // Once connected, publish an announcement...
      // 重新订阅主题
      MQTT_DEBUG_PRINTLN(this->allTopic->sub_msg_topic);
      this->client->subscribe(this->allTopic->sub_cmd_topic);
      this->client->subscribe(this->allTopic->sub_msg_topic);
      this->client->subscribe(this->allTopic->sub_bafa_topic);
      // 重新发送上线消息
      DynamicJsonDocument doc(1024);
      doc["sn"] = this->mqttInfo->sn;
      doc["type"] = "online";
      doc["ip"] = this->ip;
      String sendMsg;
      serializeJson(doc, sendMsg);
      MQTT_DEBUG_PRINTLN(sendMsg);
      this->client->publish(this->allTopic->pub_online_topic, sendMsg.c_str());
    } else {
      MQTT_DEBUG_PRINT("failed, rc=");
      MQTT_DEBUG_PRINT(client->state());
      MQTT_DEBUG_PRINTLN(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void MyMqtt::setMqttId(String id) {
  strlcpy(this->mqtt_id, id.c_str(), sizeof(this->mqtt_id));
}

void MyMqtt::loopSetMqttId(String id) {
  if (this->client->connected()) {
    this->client->disconnect();
    strlcpy(this->mqtt_id, id.c_str(), sizeof(this->mqtt_id));
    this->reconnect();
  } else {
    strlcpy(this->mqtt_id, id.c_str(), sizeof(this->mqtt_id));
  }
}